/*
 *  Copyright 2009 Coditate Software
 *  Licensed under the GNU Library General Public License (LGPL) 2.1 
 *  
 *  License available at: http://simplesavant.codeplex.com/license
 */
using System.Collections;
using System.Collections.Generic;
using Amazon.SimpleDB;
using Amazon.SimpleDB.Model;

namespace Coditate.Savant.Core
{
    /// <summary>
    /// Decorating savant implementation that transparently creates domains
    /// as necessary.
    /// </summary>
    internal class DomainCreatingSavant : DecoratingSavant
    {
        private readonly Dictionary<string, string> domains = new Dictionary<string, string>();

        /// <summary>
        /// Initializes a new instance of the <see cref="DomainCreatingSavant"/> class.
        /// </summary>
        /// <param name="decoratedSavant">The decorated savant.</param>
        public DomainCreatingSavant(ISimpleSavant2Ex decoratedSavant) : base(decoratedSavant)
        {
        }

        public override void PutAttributes(ItemMapping mapping, params PropertyValues[] values)
        {
            EnsureDomain(mapping.DomainName);

            try
            {
                DecoratedSavant.PutAttributes(mapping, values);
            }
            catch (AmazonSimpleDBException ex)
            {
                ClearIfDomainError(mapping.DomainName, ex);
                throw;
            }
        }

        public override PropertyValues GetAttributes(ItemMapping mapping, object itemName, params string[] propertyNames)
        {
            EnsureDomain(mapping.DomainName);

            try
            {
                return DecoratedSavant.GetAttributes(mapping, itemName, propertyNames);
            }
            catch (AmazonSimpleDBException ex)
            {
                ClearIfDomainError(mapping.DomainName, ex);
                throw;
            }
        }

        public override void DeleteAttributes(ItemMapping mapping, object itemName, params string[] propertyNames)
        {
            EnsureDomain(mapping.DomainName);
            try
            {
                DecoratedSavant.DeleteAttributes(mapping, itemName, propertyNames);
            }
            catch (AmazonSimpleDBException ex)
            {
                ClearIfDomainError(mapping.DomainName, ex);
                throw;
            }
        }

        public override SelectResults<PropertyValues> SelectAttributes(SelectCommand command)
        {
            EnsureDomain(command.Mapping.DomainName);
            try
            {
                return DecoratedSavant.SelectAttributes(command);
            }
            catch (AmazonSimpleDBException ex)
            {
                ClearIfDomainError(command.Mapping.DomainName, ex);
                throw;
            }
        }

        public override object SelectScalar(SelectCommand command)
        {
            EnsureDomain(command.Mapping.DomainName);
            try
            {
                return DecoratedSavant.SelectScalar(command);
            }
            catch (AmazonSimpleDBException ex)
            {
                ClearIfDomainError(command.Mapping.DomainName, ex);
                throw;
            }
        }

        private void ClearIfDomainError(string domainName, AmazonSimpleDBException error)
        {
            if (error.ErrorCode == "NoSuchDomain")
            {
                lock (((ICollection) domains).SyncRoot)
                {
                    domains.Remove(domainName);
                }
            }
        }

        private void EnsureDomain(string domainName)
        {
            if (domains.ContainsKey(domainName))
            {
                return;
            }

            var request = new CreateDomainRequest
                {
                    DomainName = domainName
                };
            SimpleDB.CreateDomain(request);
            lock (((ICollection) domains).SyncRoot)
            {
                domains[domainName] = null;
            }
        }
    }
}